/**
 * Created with CLion.
 * Description:
 * User: loong
 * Date: 2024/3/8
 * Time: 10:13
 */

#include "PluginManager.h"
#include "PluginProperty.h"
#include "algorithm.h"
#include "Content.h"

namespace CES {
    using namespace Core;
    
    namespace Internal {
        PluginManager::PluginManager()
            : contextSPtr_(std::make_shared<Content>())
        {
        }

        PluginManager::~PluginManager()
        {
            StopPlugin();
            DeletePlugin();
        }

        void PluginManager::SetPluginPaths(const QStringList & _paths)
        {
            pluginPaths_ = _paths;
        }

        void PluginManager::LoadPlugins()
        {
            // 0 读取插件元信息
            ReadPlugin();

            //1 获取加载队列(已通过依赖检测)
            auto queue = LoadQueue();

            // 2 加载
            for(auto plugin : queue)
                plugin->LoadLibrary();

            // 3 初始化
            for(auto plugin : queue)
                plugin->InitPlugin(contextSPtr_);

            //4 开始
            for(auto plugin : queue)
                plugin->StartPlugin();
        }

        void PluginManager::ReadPlugin()
        {
            allPluginsProperty_.clear();
            pluginCategory_.clear();

            // 递归获取路径下的所有库文件
            QStringList pluginFiles;
            QStringList searchPaths = pluginPaths_;

            while(!searchPaths.empty()) {
                QDir              dir(searchPaths.takeFirst());
                auto              files             = dir.entryInfoList(QDir::Files | QDir::NoSymLinks);
                const QStringList absoluteFilePaths = Utils::transform(files, &QFileInfo::absoluteFilePath);
                pluginFiles << Utils::filtered(absoluteFilePaths, [](const QString & path) { return QLibrary::isLibrary(path); });

                const QFileInfoList dirs = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
                searchPaths << Utils::transform(dirs, &QFileInfo::absoluteFilePath);
            }

            // 获取插件规范信息
            for(auto file : pluginFiles) {
                auto property = PluginProperty::ReadFile(file);
                if(nullptr == property)
                    continue;

                pluginCategory_[property->pluginSpec_->category_].append(property);
                allPluginsProperty_.append(property);
            }

            // 解决依赖
            for(auto plugin : allPluginsProperty_)
                plugin->ResolveDepends(allPluginsProperty_);
        }

        bool PluginManager::LoadQueueImpl(const PluginPropertySPtr &_spec, QVector<PluginPropertySPtr> & _queue, QVector<PluginPropertySPtr> & _checkQueue)
        {
            if(_queue.contains(_spec))
                return true;

            if(_checkQueue.contains(_spec)) {
                _spec->hasError    = true;
                _spec->errorString = "Circular dependency detected:\n";

                int index         = _checkQueue.indexOf(_spec);
                for(int i = index; i < _checkQueue.size(); i++) {
                    _spec->errorString += QString("%1 (%2) depends on\n")
                                                 .arg(_checkQueue.at(i)->pluginSpec_->name_, _checkQueue.at(i)->pluginSpec_->version_);
                }
                _spec->errorString += QString("%1 (%2) depends on\n")
                                             .arg(_spec->pluginSpec_->name_, _spec->pluginSpec_->version_);
                return false;
            }
            _checkQueue.append(_spec);

            for(auto depend : _spec->pluginDepends_) {
                if(!LoadQueueImpl(depend, _queue, _checkQueue)) {
                    _spec->hasError    = true;
                    _spec->errorString = QString("Cannot load plugin because dependency failed to load: %1 (%2)\nReason: %3")
                                                .arg(depend->pluginSpec_->name_)
                                                .arg(depend->pluginSpec_->version_)
                                                .arg(depend->errorString);
                    return false;
                }
            }

            _queue.append(_spec);
            return true;
        }

        void PluginManager::StopPlugin()
        {
            auto queue = LoadQueue();
            for(auto plugin : queue)
                plugin->StopPlugin();
        }

        void PluginManager::DeletePlugin()
        {
            auto queue = LoadQueue();
            for(auto plugin : queue)
                plugin->UnInitPlugin();

            allPluginsProperty_.clear();
            pluginCategory_.clear();
        }

        QVector<PluginPropertySPtr> PluginManager::LoadQueue()
        {
            QVector<PluginPropertySPtr> queue;
            for(auto pro : qAsConst(allPluginsProperty_)) {
                QVector<decltype(pro)> circularityCheckQueue;
                LoadQueueImpl(pro, queue, circularityCheckQueue);
            }
            return queue;
        }

    }// namespace CES
    
    namespace Core{
        IPluginManagerSPtr Core::IPluginManager::GetInstance()
        {
            static auto singleton = std::make_shared<Internal::PluginManager>();
            return singleton;
        }
    }// namespace Core
}// namespace CES